package hnx.ca.util;

/**
 * 
 */

/**
 * @author ZOM TUNG
 *
 */
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import sun.security.x509.X509CRLImpl;

public class VerifyUtils {

//	public static final String CERTS_FOR_DIRECT_VALIDATION_DIR = XFireServletController
//			.getRequest().getRealPath("WEB-INF").replace('\\', '/')
//			+ "/certs-for-direct-validation";
//	public static final String TRUSTED_CA_ROOT_CERTS_DIR = XFireServletController
//			.getRequest().getRealPath("WEB-INF").replace('\\', '/')
//			+ "/trusted-CA-root-certs";
//	public static final String TRUSED_ISSUER_URL_PATH_FILE = XFireServletController
//			.getRequest().getRealPath("WEB-INF").replace('\\', '/')
//			+ "/TrustedIsserURLPath.txt";
//
//	public static final String TRUSED_REVOCATION_LIST_URL_FILE = XFireServletController
//			.getRequest().getRealPath("WEB-INF").replace('\\', '/')
//			+ "/TrustedRevocationListURL.txt";

	 public static final String CERTS_FOR_DIRECT_VALIDATION_DIR =
	 "./certs-for-direct-validation";
	 public static final String TRUSTED_CA_ROOT_CERTS_DIR =
	 "./trusted-CA-root-certs";
	 public static final String TRUSED_ISSUER_URL_PATH_FILE =
	 "./TrustedIsserURLPath.txt";

	private static final int KEY_USAGE_DIGITAL_SIGNATURE = 0;
	private static final int KEY_USAGE_NON_REPUDIATION = 1;
	private static final int KEY_USAGE_KEY_ENCIPHERMENT = 2;
	private static final int KEY_USAGE_DATA_ENCIPHERMENT = 3;
	private static final int KEY_USAGE_KEY_AGREEMENT = 4;
	private static final int KEY_USAGE_CERT_SIGN = 5;
	private static final int KEY_USAGE_CRL_SIGN = 6;
	private static final int KEY_USAGE_ENCIPHER_ONLY = 7;
	private static final int KEY_USAGE_DECIPHER_ONLY = 8;

	private static final String X509_CERTIFICATE_TYPE = "X.509";
	private static final String CERT_CHAIN_ENCODING = "PkiPath";
	private static final String DIGITAL_SIGNATURE_ALGORITHM_NAME = "SHA1withRSA";
	private static final String CERT_CHAIN_VALIDATION_ALGORITHM = "PKIX";

	// private byte[] mReceivedFileData = null;
	// private static CertPath mCertPath = null;
	// private static X509Certificate[] mCertChain = null;
	// private static X509Certificate mCertificate = null;
	// private byte[] mSignature = null;
	// private String mSignatureBase64Encoded;

	public static X509Certificate loadX509CertificateFromBase64String(
			String certChainBase64Encoded) throws CertificateException,
			IOException {
		X509Certificate mCertificate = null;

		CertPath mCertPath = loadCertPathFromBase64String(certChainBase64Encoded);
		@SuppressWarnings("rawtypes")
		List certsInChain = mCertPath.getCertificates();
		X509Certificate[] mCertChain = (X509Certificate[]) certsInChain
				.toArray(new X509Certificate[0]);
		mCertificate = mCertChain[0];

		return mCertificate;
	}

	public static X509Certificate[] loadX509CertificationChainFromBase64String(
			String certChainBase64Encoded) throws Exception {
		X509Certificate[] mCertChain;
		CertPath mCertPath = loadCertPathFromBase64String(certChainBase64Encoded);
		List certsInChain = mCertPath.getCertificates();
		mCertChain = (X509Certificate[]) certsInChain
				.toArray(new X509Certificate[0]);
		return mCertChain;
	}

	public static void displayCertificationChain(X509Certificate[] aCertChain)
			throws IOException {
		System.out.println("Certification chain length: " + aCertChain.length);
		for (int i = 0; i < aCertChain.length; i++) {
			Principal certPrincipal = aCertChain[i].getSubjectDN();
			System.out.println("certChain[" + i + "] = " + certPrincipal);
		}
	}

	public static void displayCertificate(X509Certificate aCertificate)
			throws IOException {
		String full = aCertificate.toString();
		String certificateSubject = aCertificate.getSubjectDN().toString();
		String serialNumber = aCertificate.getSerialNumber().toString();
		String publicKey = aCertificate.getPublicKey().toString();
		String aSignature = aCertificate.getSignature().toString();
		System.out.println("Certificate subject: " + certificateSubject
				+ " <br>");
		System.out.println("Serial Number: " + serialNumber + " <br>");
		System.out.println("Public Key: " + publicKey + " <br>");
		System.out.println("Signature: " + aSignature + " <br>");
		boolean[] certKeyUsage = aCertificate.getKeyUsage();
		System.out.println("Certificate purposes (public key usages): <br>");
		if (certKeyUsage != null) {
			if (certKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE])
				System.out
						.println("[digitalSignature] - verify digital signatures <br>");
			if (certKeyUsage[KEY_USAGE_NON_REPUDIATION])
				System.out
						.println("[nonRepudiation] - verify non-repudiation <br>");
			if (certKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT])
				System.out
						.println("[keyEncipherment] - encipher keys for transport <br>");
			if (certKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT])
				System.out
						.println("[dataEncipherment] - encipher user data <br>");
			if (certKeyUsage[KEY_USAGE_KEY_AGREEMENT])
				System.out
						.println("[keyAgreement] - use for key agreement <br>");
			if (certKeyUsage[KEY_USAGE_CERT_SIGN])
				System.out
						.println("[keyCertSign] - verify signatures on certificates <br>");
			if (certKeyUsage[KEY_USAGE_CRL_SIGN])
				System.out
						.println("[cRLSign] - verify signatures on CRLs <br>");
			if (certKeyUsage[KEY_USAGE_ENCIPHER_ONLY])
				System.out
						.println("[encipherOnly] - encipher during key agreement <br>");
			if (certKeyUsage[KEY_USAGE_DECIPHER_ONLY])
				System.out
						.println("[decipherOnly] - decipher during key agreement <br>");
		} else {
			System.out.println("[No purposes defined] <br>");
		}
	}

	public static boolean verifyReceivedCertificate(X509Certificate mCertificate)
			throws CertificateValidationException, CertificateException,
			IOException {
		// Create the list of the trusted certificates for direct validation
		X509Certificate[] trustedCertificates;
		trustedCertificates = getCertificateList(CERTS_FOR_DIRECT_VALIDATION_DIR);
		verifyCertificate(mCertificate, trustedCertificates);
		System.out.println("Certificate is verified to be VALID.");
		return true;
	}

	/**
	 * Verifies received certification chain and displays the verification
	 * results. The chain for verification is taken form mCertPath member
	 * variable. Trusted CA root certificates are taken from the
	 * TRUSTED_CA_ROOT_CERTS_DIR directory. This directory should be relative to
	 * the Web application root directory and should contain only .CER files
	 * (DER-encoded X.509 certificates).
	 * 
	 * @throws CertificateException
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidAlgorithmParameterException
	 * @throws CertPathValidatorException
	 */
	public static boolean verifyReceivedCertificationChain(CertPath mCertPath)
			throws CertificateException, IOException,
			CertPathValidatorException, InvalidAlgorithmParameterException,
			NoSuchAlgorithmException {
		// Create the most trusted CA set of trust anchors
		X509Certificate[] trustedCACerts;
		trustedCACerts = getCertificateList(TRUSTED_CA_ROOT_CERTS_DIR);
		verifyCertificationChain(mCertPath, trustedCACerts);
		return true;
	}

	/**
	 * @return a list of X509 certificates, obtained by reading all files from
	 *         the given directory. The supplied directory should be a given as
	 *         a relative path from the Web appication root (e.g.
	 *         "/WEB-INF/test") and should contain only .CER files (DER-encoded
	 *         X.509 certificates).
	 */

	public static String[] readTrustedCerts(String aCertificatesDirectory) {
		String[] nameArray;
		File dir = new File(aCertificatesDirectory);
		String[] chld = dir.list();
		for (int i = 0; i < chld.length; i++) {
			chld[i] = aCertificatesDirectory + "\\" + chld[i];
		}
		return chld;

	}



	public static X509Certificate[] getCertificateList(
			String aCertificatesDirectory) throws CertificateException,
			IOException {
		// Get a list of all files in the given directory

		String[] trustedCertsResourceNames = readTrustedCerts(aCertificatesDirectory);

		// Allocate an array for storing the certificates
		int count = trustedCertsResourceNames.length;
		X509Certificate[] trustedCertificates = new X509Certificate[count];

		// Read all X.509 certificate files one by one into an array
		for (int i = 0; i < count; i++) {
			String certResName = trustedCertsResourceNames[i];
			// InputStream certStream =
			// aServletContext.getResourceAsStream(certResName);
			// System.out.println(certResName);
			InputStream certStream = new FileInputStream(certResName);
			try {
				X509Certificate trustedCertificate = loadX509CertificateFromStream(certStream);
				trustedCertificates[i] = trustedCertificate;
				// displayCertificate(trustedCertificate);
				// System.out.println("*******************************");
			} finally {
				certStream.close();
			}
		}
		return trustedCertificates;
	}

	public static X509Certificate[] getCertificateChainFromCert(
			X509Certificate cert) throws CertificateException, IOException {
		ArrayList<X509Certificate> certArr = new ArrayList<X509Certificate>();
		boolean hasCert = true;
		X509Certificate issuerCert = null;
		int count = 0;
		while (hasCert) {
			certArr.add(cert);
			issuerCert = null;
			issuerCert = getIssuerCertificate(cert);
			if (issuerCert != null)
				cert = issuerCert;
			else
				hasCert = false;
		}
		int numCert = certArr.size();
		X509Certificate[] resultArr = new X509Certificate[numCert];
		for (int i = 0; i < numCert; i++) {
			resultArr[i] = certArr.get(i);
		}
		// khong ep duoc kieu
		return resultArr;
		// return null;
	}

	public static boolean verifyReceivedSignature(byte[] mReceivedData,
			X509Certificate mCertificate, byte[] mSignature)
			throws InvalidKeyException, NoSuchAlgorithmException,
			SignatureException {
		boolean signatureValid = false;
		signatureValid = verifyDocumentSignature(mReceivedData, mCertificate,
				mSignature);
		return signatureValid;
	}

	public static boolean verifyCertInRevocationListFromFile(
			X509Certificate cert, String CRLFileName)
			throws FileNotFoundException, CRLException {
		FileInputStream fis = new FileInputStream(CRLFileName);
		sun.security.x509.X509CRLImpl x = new X509CRLImpl(fis);
		return x.isRevoked(cert);
	}

	public static boolean verifyCertInRevocationListFromURLPath(
			X509Certificate cert, String URLPath) throws MalformedURLException,
			IOException, CRLException {
		URL url;
		boolean result = true;
		url = new URL(URLPath);
		InputStream is = url.openStream();
		sun.security.x509.X509CRLImpl x = new X509CRLImpl(is);
		result = x.isRevoked(cert);
		return result;
	}

	public static byte[] getURLFileAsByteArray(URL url) throws IOException {
		URLConnection connection = url.openConnection();
		// Since you get a URLConnection, use it to get the InputStream
		InputStream in = connection.getInputStream();
		// Now that the InputStream is open, get the content length
		int contentLength = connection.getContentLength();

		// To avoid having to resize the array over and over and over as
		// bytes are written to the array, provide an accurate estimate of
		// the ultimate size of the byte array
		ByteArrayOutputStream tmpOut;
		if (contentLength != -1) {
			tmpOut = new ByteArrayOutputStream(contentLength);
		} else {
			tmpOut = new ByteArrayOutputStream(16384);
		}

		byte[] buf = new byte[512];
		while (true) {
			int len = in.read(buf);
			if (len == -1) {
				break;
			}
			tmpOut.write(buf, 0, len);
		}
		in.close();
		tmpOut.close();

		byte[] array = tmpOut.toByteArray();
		return array;
	}

	public static X509Certificate loadX509CertificateFromStream(
			InputStream aCertStream) throws CertificateException {
		CertificateFactory cf = CertificateFactory
				.getInstance(X509_CERTIFICATE_TYPE);
		X509Certificate cert = (X509Certificate) cf
				.generateCertificate(aCertStream);
		return cert;
	}

	public static X509Certificate loadX509CertificateFromURLPath(String URLPath)
			throws IOException, CertificateException {
		URL url = new URL(URLPath);
		byte[] certData = getURLFileAsByteArray(url);
		InputStream is = url.openStream();
		X509Certificate cert = loadX509CertificateFromStream(is);
		return cert;
	}

	public static CertPath loadCertPathFromBase64String(
			String aCertChainBase64Encoded) throws CertificateException,
			IOException {
		
		byte[] certChainEncoded = Base64Utils
				.base64Decode(aCertChainBase64Encoded);
		
//		String encode = new String(certChainEncoded);
//		System.out.println(encode);
		
		CertificateFactory cf = CertificateFactory
				.getInstance(X509_CERTIFICATE_TYPE);
		InputStream certChainStream = new ByteArrayInputStream(certChainEncoded);
		CertPath certPath;
		try {
			certPath = cf
					.generateCertPath(certChainStream, CERT_CHAIN_ENCODING);
		} finally {
			certChainStream.close();
		}
		return certPath;
	}

	public static CertPath loadCertPathFromCertificateChain(
			X509Certificate[] cert) throws CertificateException {
		CertificateFactory cf = CertificateFactory
				.getInstance(X509_CERTIFICATE_TYPE);
		CertPath certPath = null;
		List<X509Certificate> certificatesList = new ArrayList<X509Certificate>();
		for (int i = 0; i < cert.length; i++) {
			certificatesList.add(cert[i]);
		}
		certPath = cf.generateCertPath(certificatesList);
		return certPath;
	}

	public static X509Certificate loadX509CertificateFromCERFile(
			String aFileName) throws GeneralSecurityException, IOException {
		FileInputStream fis = new FileInputStream(aFileName);
		X509Certificate cert = null;
		try {
			cert = loadX509CertificateFromStream(fis);
		} finally {
			fis.close();
		}
		return cert;
	}

	public static CertPath removeLastCertFromCertChain(CertPath aCertChain)
			throws CertificateException {
		List certs = aCertChain.getCertificates();
		int certsCount = certs.size();
		List certsWithoutLast = certs.subList(0, certsCount - 1);
		CertificateFactory cf = CertificateFactory
				.getInstance(X509_CERTIFICATE_TYPE);
		CertPath certChainWithoutLastCert = cf
				.generateCertPath(certsWithoutLast);
		return certChainWithoutLastCert;
	}

	public static void verifyCertificate(X509Certificate aCertificate)
			throws GeneralSecurityException {
		// First check certificate validity period
		aCertificate.checkValidity();
	}

	public static void verifyCertificate(X509Certificate aCertificate,
			X509Certificate[] aTrustedCertificates)
			throws CertificateExpiredException,
			CertificateNotYetValidException, CertificateValidationException {
		// First check certificate validity period
		aCertificate.checkValidity();
		// Check if the certificate is signed by some of the given trusted
		// certificates
		for (int i = 0; i < aTrustedCertificates.length; i++) {
			X509Certificate trustedCert = aTrustedCertificates[i];
			try {
				aCertificate.verify(trustedCert.getPublicKey());
				// Found parent certificate. Certificate is verified to be valid
				return;
			} catch (GeneralSecurityException ex) {

				// Certificate is not signed by current trustedCert. Try the
				// next one
			}
		}
		// Certificate is not signed by any of the trusted certs --> it is
		// invalid
		throw new CertificateValidationException(
				"Can not find trusted parent certificate.");
	}

	public static void verifyCertificationChain(CertPath aCertChain,
			X509Certificate[] aTrustedCACertificates)
			throws CertPathValidatorException,
			InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			CertificateException {
		int chainLength = aCertChain.getCertificates().size();
		if (chainLength < 2) {
			throw new CertPathValidatorException(
					"The certification chain is too "
							+ "short. It should consist of at least 2 certiicates.");
		}

		// Create a set of trust anchors from given trusted root CA certificates
		HashSet trustAnchors = new HashSet();
		for (int i = 0; i < aTrustedCACertificates.length; i++) {
			TrustAnchor trustAnchor = new TrustAnchor(
					aTrustedCACertificates[i], null);
			trustAnchors.add(trustAnchor);

		}

		// Create a certification chain validator and a set of parameters for it
		PKIXParameters certPathValidatorParams = new PKIXParameters(
				trustAnchors);
		certPathValidatorParams.setRevocationEnabled(false);
		CertPathValidator chainValidator = CertPathValidator
				.getInstance(CERT_CHAIN_VALIDATION_ALGORITHM);

		// Remove the root CA certificate from the end of the chain. This is
		// required
		// by the validation algorithm because by convention the trust anchor
		// certificates should not be a part of the chain that is validated
		CertPath certChainForValidation = removeLastCertFromCertChain(aCertChain);

		// Execute the certification chain validation
		chainValidator
				.validate(certChainForValidation, certPathValidatorParams);

	}

	public static boolean verifyDocumentSignature(byte[] aDocument,
			PublicKey aPublicKey, byte[] aSignature, String signAlgorithm)
			throws NoSuchAlgorithmException, InvalidKeyException,
			SignatureException {
		Signature signatureAlgorithm = Signature
				.getInstance(DIGITAL_SIGNATURE_ALGORITHM_NAME);
		signatureAlgorithm.initVerify(aPublicKey);
		signatureAlgorithm.update(aDocument);
		boolean valid = signatureAlgorithm.verify(aSignature);
		return valid;
	}

	public static boolean verifyDocumentSignature(byte[] aDocument,
			X509Certificate aCertificate, byte[] aSignature)
			throws InvalidKeyException, NoSuchAlgorithmException,
			SignatureException {
		PublicKey publicKey = aCertificate.getPublicKey();
		String signatureAlgorithm = aCertificate.getSigAlgName();
		boolean valid = verifyDocumentSignature(aDocument, publicKey,
				aSignature, signatureAlgorithm);
		return valid;
	}

	public static class CertificateValidationException extends
			GeneralSecurityException {
		public CertificateValidationException(String aMessage) {
			super(aMessage);
		}

		public CertificateValidationException(String aMessage, Throwable aCause) {
			super(aMessage, aCause);
		}
	}

	public static String getIssuerCertificateURLPath(X509Certificate cert) {
		String tempIssuerCertificateURLPath = null;
		try {
			tempIssuerCertificateURLPath = new String(cert
					.getExtensionValue("1.3.6.1.5.5.7.1.1"));
		} catch (Exception e) {
			return null;
		}
		if (tempIssuerCertificateURLPath != null) {
			int firtIndex = tempIssuerCertificateURLPath.indexOf("http://");
			int lastIndexCER = tempIssuerCertificateURLPath.lastIndexOf(".cer");
			int lastIndexCRT = tempIssuerCertificateURLPath.lastIndexOf(".crt");
			if (firtIndex > 0) {
				if (lastIndexCER > 0) {
					return tempIssuerCertificateURLPath.substring(firtIndex,
							lastIndexCER)
							+ ".cer";
				}
				if (lastIndexCRT > 0) {
					return tempIssuerCertificateURLPath.substring(firtIndex,
							lastIndexCRT)
							+ ".crt";
				}
			}
		}
		return null;
	}

	public static X509Certificate getIssuerCertificate(X509Certificate cert)
			throws CertificateException, IOException {
		String issuerCertificateURLPath = getIssuerCertificateURLPath(cert);
		System.out.println("issuerCertificateURLPath"
				+ issuerCertificateURLPath);
		X509Certificate issuerCertificate = null;
		if (issuerCertificateURLPath != null) {
			issuerCertificate = loadX509CertificateFromURLPath(issuerCertificateURLPath);
		}
		return issuerCertificate;
	}

	public static String getIssuerRevocationListURLPath(X509Certificate cert) {

		String tempIssuerCertificateURLPath = new String(cert
				.getExtensionValue("2.5.29.31"));
		int firtIndex = tempIssuerCertificateURLPath.indexOf("http://");
		int lastIndexCRL = tempIssuerCertificateURLPath.lastIndexOf(".crl");
		if (firtIndex > 0)
			if (lastIndexCRL > 0)
				return tempIssuerCertificateURLPath.substring(firtIndex,
						lastIndexCRL)
						+ ".crl";

		return null;
	}

	public static boolean checkIssuerURLPath(String IssuerURLPath,
			String fileName) throws IOException {
		FileInputStream fis = new FileInputStream(fileName);
		DataInputStream dis = new DataInputStream(fis);
		String str;
		while ((str = dis.readLine()) != null) {
			if (str.equalsIgnoreCase(IssuerURLPath)) {
				dis.close();
				fis.close();
				return true;
			}
		}
		return false;
	}

	public static int verifySignatureOfIssuer(X509Certificate cert)
			throws IOException {
		String issuerCertPath = getIssuerCertificateURLPath(cert);
		if (issuerCertPath == null) {
			System.out.println("Cert khong co issuerCertPath!");
			return 5;
		}
		boolean checkTrustedIssuerCertPath = checkIssuerURLPath(issuerCertPath,
				TRUSED_ISSUER_URL_PATH_FILE);
		if (!checkTrustedIssuerCertPath) {
			System.out
					.println("issuerCertPath khong nam trong danh sach tin cay!");
			return 6;
		}
		X509Certificate issuerCert;
		try {
			issuerCert = loadX509CertificateFromURLPath(issuerCertPath);
			PublicKey issuerPublicKey = issuerCert.getPublicKey();
			cert.verify(issuerPublicKey);
			return 0;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("Loi khi lay issuerCert");
			e.printStackTrace();
			return 7;
		} catch (GeneralSecurityException e) {
			// TODO Auto-generated catch block
			System.out.println("Loi khi verify issuerPublicKey");
			e.printStackTrace();
			return 8;
		}
	}

	public static int verifyTime(X509Certificate cert) {

		try {
			cert.checkValidity();
			return 0;
		} catch (CertificateExpiredException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 1;
		} catch (CertificateNotYetValidException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 2;
		}
	}

	public static boolean verifyCRL(X509Certificate cert) {		
		try {
			CRLVerifier.verifyCertificateCRLs(cert);
		} catch (CertificateVerificationException e) {
			return false;
		}		 
		return true;
	}
	
	public static String verifyBased64String(String base64String) {
		try {
			byte[] data = Base64Utils.base64Decode(base64String); 
			//return verifyXMLString(new String(data, "ISO-8859-1"));
			String xml =  new String(data,"ISO-8859-1");
			System.out.println(xml);
			return verifyXMLString(xml);
		} catch (Exception e) {
			// TODO Auto-generated catch block
		//	e.printStackTrace();
			return verifyXMLString(new String(Base64Utils.base64Decode(base64String)));
		}
	}

	public static String verifyXMLString(String xml) {
		String digitalSignatureBase64Encoded = xml.substring(xml.indexOf("<SignatureValue>") + "<SignatureValue>".length(), xml.indexOf("</SignatureValue>")); 
		String certChainBase64Encoded = xml.substring(xml.indexOf("<X509Certificate>") + "<X509Certificate>".length(), xml.indexOf("</X509Certificate>"));
		
		String inputData = xml.substring(xml.indexOf("<Form>/n") + "<Form>/n".length(), xml.indexOf("\n  <Signature>"));
		inputData = inputData.trim();
		String verifyResult = verifyReceiveData(inputData, certChainBase64Encoded, digitalSignatureBase64Encoded);
		return verifyResult;
	}
	

	public static String verifyReceiveData(String data,
			String certChainBase64Encoded, String digitalSignatureBase64Encoded) {
		CertPath mCertPath = null;
		X509Certificate mCertificate = null;
		X509Certificate[] mCertChain = null;
		boolean checkCertificationChain = false;
		boolean checkSignature = false;
		boolean checkCertificate = false;
		boolean checkRevocationList = false;		

		boolean continued = true;
		try {
			mCertPath = VerifyUtils
					.loadCertPathFromBase64String(certChainBase64Encoded);
			List certsInChain = mCertPath.getCertificates();
			mCertChain = (X509Certificate[]) certsInChain
					.toArray(new X509Certificate[0]);
			mCertificate = mCertChain[0];
			byte[] messageData = data.getBytes();
			byte[] aSignatureature = Base64Utils
					.base64Decode(digitalSignatureBase64Encoded);
			// verify digital signature
			if (continued) {
				checkSignature = VerifyUtils.verifyReceivedSignature(messageData,
						mCertificate, aSignatureature);
				if (!checkSignature) {
					continued = false;
					return "DIGITAL SIGNATURE VERIFY FAIL";
				}
			}
			// verify revecived Certificate
			// them phan nay vao sau khi config duoc duong dan toi cac trusted certificates
/*			if (continued) {
				checkCertificate = VerifyUtils
						.verifyReceivedCertificate(mCertificate);
				if (!checkCertificate) {
					continued = false;
					return "CERTIFICATE VERIFY FAIL";
				}
			}
			*/
			// check Revocation List
			
			if (continued) {
				checkRevocationList = VerifyUtils.verifyCRL(mCertificate);
				if (!checkRevocationList) {
					continued = false;
					return "INVALID CERTIFICATE";
				}
			}

		} catch (Exception e) {
		//	e.printStackTrace();
			return e.getMessage();
		}		
		return "VERIFY SUCCESSFUL";
	}

	public static void main(String[] args) {
		String data = "PEZvcm0+CiAgPElEPkNJTVMxMDAwODg4PC9JRD4KICA8U2lnbmF0dXJlPgogICAgPHhtbG5zPmh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyM8L3htbG5zPgogICAgPFNpZ25lZEluZm8+CiAgICAgIDxDYW5vbmljYWxpemF0aW9uTWV0aG9kQWxnb3JpdGhtPmh0dHA6Ly93d3cudzMub3JnL1RSLzIwMDEvUkVDLXhtbC1jMTRuLTIwMDEwMzE1PC9DYW5vbmljYWxpemF0aW9uTWV0aG9kQWxnb3JpdGhtPgogICAgICA8U2lnbmF0dXJlTWV0aG9kQWxnb3JpdGhtPmh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMTwvU2lnbmF0dXJlTWV0aG9kQWxnb3JpdGhtPgogICAgICA8UmVmZXJlbmNlPgogICAgICAgIDxVUkk+PC9VUkk+CiAgICAgICAgPFRyYW5zZm9ybUFsZ29yaXRobT5odHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZTwvVHJhbnNmb3JtQWxnb3JpdGhtPgogICAgICAgIDxEaWdlc3RNZXRob2RBbGdvcml0aG0+aHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTE8L0RpZ2VzdE1ldGhvZEFsZ29yaXRobT4KICAgICAgICA8RGlnZXN0VmFsdWU+VlFHK1hmQ0FKZDNPNHMxczg2TUo5TmE3anFnPTwvRGlnZXN0VmFsdWU+CiAgICAgIDwvUmVmZXJlbmNlPgogICAgPC9TaWduZWRJbmZvPgogICAgPFNpZ25hdHVyZVZhbHVlPlIrVDhsR3NlMTA3ZlkxMEJhZmpYMEVDR3duclA2U0ZXc1gyMVAvVmlpQVBlWXdkcHJIaDYxQlNXMDc5MjM1MWNVMmtiNlRLMlorampPcFdLbmc5dnAyMzdRVUpVNXRMZEtFeVNOT00zV1hvRkF4N09UUEJPRjlDRTlQNnZkUXN4R3lCSUZsaDRtWGpwbXVkdDdYNzJHbzB1K1VQMEp2bnFLek1EMm1xWjVHVT08L1NpZ25hdHVyZVZhbHVlPgogICAgPEtleUluZm8+CiAgICAgIDxYNTA5RGF0YT4KICAgICAgICA8WDUwOVN1YmplY3ROYW1lPlVJRD1DTU5EOjAwMTEsIENOPVRlc3QsIEw9QmEgZGluaCwgU1Q9SOAgTj9pLCBDPVZOPC9YNTA5U3ViamVjdE5hbWU+CiAgICAgICAgPFg1MDlDZXJ0aWZpY2F0ZT5NSUlGampDQ0JZb3dnZ055b0FNQ0FRSUNFRlFCMnlTekhXK3diQ2NYS2o1Q3M3NHdEUVlKS29aSWh2Y05BUUVGQlFBd2FURUxNQWtHQTFVRUJoTUNWazR4RXpBUkJnTlZCQW9UQ2xaT1VGUWdSM0p2ZFhBeEhqQWNCZ05WQkFzVEZWWk9VRlF0UTBFZ1ZISjFjM1FnVG1WMGQyOXlhekVsTUNNR0ExVUVBeE1jVms1UVZDQkRaWEowYVdacFkyRjBhVzl1SUVGMWRHaHZjbWwwZVRBZUZ3MHhNVEE0TVRrd09ERTNNREJhRncweE1qQTRNVGd3T0RFM01EQmFNRjB4Q3pBSkJnTlZCQVlUQWxaT01SSXdFQVlEVlFRSURBbEl3NkFnVHVHN21Xa3hFREFPQmdOVkJBY01CMEpoSUdScGJtZ3hEVEFMQmdOVkJBTU1CRlJsYzNReEdUQVhCZ29Ka2lhSmsvSXNaQUVCREFsRFRVNUVPakF3TVRFd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFJTHByUzYzd2FBbE5HYmwxMmd6dHFNTUM0bFREQ0ZWNWp6akQ4REtUTHpFMHdRZnBRNWFuNlJMeFp2b0xxS3J0THRiWXZvQlppc0ZjN2FjK1hGQWJ0cXQ1blpTOXlOTi9hM3lIRHkweURjSmxrZEh6WC8vYmI3WUE4UUEwazltTmtBN1B5UzFvZHJ3WlZrWVlpeU5aQkZHUkpncnJNcjhXVEl6d1JjUnIvMXZBZ01CQUFHamdnRzhNSUlCdURCd0JnZ3JCZ0VGQlFjQkFRUmtNR0l3TWdZSUt3WUJCUVVITUFLR0ptaDBkSEE2THk5d2RXSXVkbTV3ZEMxallTNTJiaTlqWlhKMGN5OTJibkIwWTJFdVkyVnlNQ3dHQ0NzR0FRVUZCekFCaGlCb2RIUndPaTh2YjJOemNDNTJibkIwTFdOaExuWnVMM0psYzNCdmJtUmxjakFkQmdOVkhRNEVGZ1FVUG9rdnFhTmpzWk5MSjVQcExpMUgycEhqZTlNd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JRR2FjRFYxUUtLRlkxR2ZlbDg0bWdLVmF4cXJ6QjJCZ05WSFNBRWJ6QnRNRFVHQ3lzR0FRUUJnZm82QWdFQ01DWXdKQVlJS3dZQkJRVUhBZ0l3R0I0V0FGQUFTUUJFQUMwQVFnQXhBQzRBTUFBdEFERUFlVEEwQmdrckJnRUVBWUg2T2dJd0p6QWxCZ2dyQmdFRkJRY0NBUllaYUhSMGNEb3ZMM0IxWWk1MmJuQjBMV05oTG5adUwzSndZVEF4QmdOVkhSOEVLakFvTUNhZ0pLQWloaUJvZEhSd09pOHZZM0pzTG5adWNIUXRZMkV1ZG00dmRtNXdkR05oTG1OeWJEQU9CZ05WSFE4QkFmOEVCQU1DQlBBd0h3WURWUjBsQkJnd0ZnWUlLd1lCQlFVSEF3UUdDaXNHQVFRQmdqY0tBd3d3R2dZRFZSMFJCQk13RVlFUGRHVnpkRUIyWkdNdVkyOXRMblp1TUEwR0NTcUdTSWIzRFFFQkJRVUFBNElDQVFBNUhvMXVHSUw2anVLZHBKYVdZUTlNQVdCczAxL2YwVEE1TjZaUmFlVGJZckxacDdFSEVyS1VYUmlweWdnTS9iZG54alEzWnVDN2N2RDRnMUFucEdVMUwyL1RJYUczeUI1UzVoOE4zZlh2K01qN3NLWXB6SG5aeGFKYjIvQkRGL2t5RGptbmFyd2dXWU55YXJncVpJSmhYOTlndWE1TDhFMEpwemJ5dzNRQlBsbWN1NWtCQUxLMXc0aUtGbWNzVDR1MWU5ZzFXV1Z1ZU85Yy9adGp1U2N2NVY2SVVvUDJka0JuSCtMQkRCdVlEQUhFR1pkL0EwU3Y1aU9rZjgzcjl5TlJJdHVkSFNkbEVQUTQ0dFhpUHc5SXdqOEV0RUsrWWlWQThhdk52QmM5MW1EcE1xVWxGb3Fxby9vcUloRVMrVlNUa3ZDdnVwMFVxZjZDc1N4c1pRMDh3VWRkMDZ4a0x3R2dhSTNoZEdPZzFBY1k1R2x5TWlyT2VSM2MxblZSTzV5dk9nL2ZhaStKQ29KV3EyUGNCU1FJRTBrdVlPZDkwRjI5b3cyQWxJQ2IwR2E3d2x6Z2VETGNFMGYyL3JzaU8wSzlBOTMwd3dZQjJEdk9xbFRZaHRMV2JPaERURDJEa0duTFQ1QUJENFl6NnF4eGFUWEJ4SjYranVEWjVpbUNhajF1c2RlL0dtc1RuUHdOV2tZenQ5dUJGT1JoL2FtYnRYR0V3dm03R2dxMWF5ZmNvcDBLVnpnY1JXd0x0bjR1OUh0QlhCOUFVQ21FbHF1SXMvZ1JwWGdNVWRUaDhTZk41VUlKNnRkRUFHdGJodVdJSW1CbmpZRkROcTJaZEc4a3hUdHRzYWZSOXpPUFNjYnpmakZiWkRvN3R1Yk16TEVZUS9yN1I4di91T2s5WXc9PTwvWDUwOUNlcnRpZmljYXRlPgogICAgICA8L1g1MDlEYXRhPgogICAgPC9LZXlJbmZvPgogIDwvU2lnbmF0dXJlPgo8L0Zvcm0+";
		String data1 = "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";
		System.out.println(verifyBased64String(data));

	}

}